#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "handle.h"

// 是否分块传输
int is_chunk = 0;

// 分析请求报文
int parse_buffer(char *buf, http_head *hhead, req_head *rhead, char *data_buf) {
    char *cur = buf;
    char *next = NULL;
    char *tmp = NULL;

    // 消除头部
    tmp = strstr(buf, "\r\n\r\n");
    *tmp = '\0';
    tmp = tmp + 4;
    strcpy(data_buf, tmp);
    // 解析固定头部
    next = strstr(cur, "\r\n");
    if(next != NULL) {
        *next = '\0';
        next += 2;
    }

    // 根据" "以此获得需要的值，有点像python里的split
    tmp = strtok(cur, " ");
    strcpy(hhead->method, tmp);
    tmp = strtok(NULL, " ");
    strcpy(hhead->url, tmp);
    tmp = strtok(NULL, " ");
    strcpy(hhead->version, tmp);

    // 解析变长头部
    while(next != NULL) {
        cur = next;
        next = strstr(cur, "\r\n");
        if(next != NULL) {
            *next = '\0';
            next = next + 2;
        }

        if(rhead->num > 16) {
            printf("Too much head info!\n\n");
            return -1;
        }

        tmp = NULL;
        strtok_r(cur, ":", &tmp);
        strcpy(rhead->line[rhead->num].name, cur);
        strcpy(rhead->line[rhead->num].value, tmp+1);
        rhead->num++;
    }
}

// 选择method 本次实验只需实现GET
int get_method(char *method) {
    if(!strcmp("GET", method))
        return 1;
    else if(!strcmp("POST", method))
        return 2;
    else return -1;
}

// 判断是否处于连接状态
int is_alive(req_head *rhead) {
    for(int i=0; i < rhead->num; i++) {
        // printf("%s: %s\n", rhead->line[i].name, rhead->line[i].value);
        if(!strcmp(rhead->line[i].name, "Connection") && !strcmp(rhead->line[i].value, "Keep-Alive"))
            return 1;
    }
    return 0;
}

// 根据url找寻请求文件
int copy_file(http_head *h, char *buf) {
    FILE *f = fopen(h->url+1, "r");
    // 没有找到文件 接404处理
    if(f == NULL) {
        printf("No file in server!\n\n");
        return -1;
    }

    printf("Open %s.\n", h->url+1);

    // 找到文件  读入buf 并 计算大小
    int fsize = fread(buf, 1, 20*1024*1024, f);
    printf("file size : %d\n\n", fsize);
    return fsize;
}

// 数据传送-分块传输
int ack_get(int sockid, char *ack_buf, char *file_buf, int fsize) {
    int times = 0;
    if(is_chunk == 0) {
        //printf("Trans without chunk.\n");
        int len = strlen(ack_buf) + fsize;
        memcpy(ack_buf + strlen(ack_buf), file_buf, fsize);
        write(sockid, ack_buf, len);
        printf("Trans Data finished!\n\n");
        return 0;
    }
    printf("Trans with chunk.\n");
    // 分块 - 每次传输512字节
    int cur = 0; //当前字节数
    int chunk; //当前轮传输字节数
    while(cur < fsize) {
        chunk = ((fsize - cur) < 512) ? (fsize - cur) : 512;
        printf("Chunk %d: size %d.\n", times++, chunk);
        sprintf(ack_buf + strlen(ack_buf), "%x\r\n", chunk);
        memcpy(ack_buf + strlen(ack_buf), file_buf + cur, chunk);
        strcpy(ack_buf + strlen(ack_buf), "\r\n");
        write(sockid, ack_buf, strlen(ack_buf));
        // 每轮清空buf
        memset(ack_buf, 0, 1024);
        cur += chunk;
    }
    strcpy(ack_buf + strlen(ack_buf), "0\r\n\r\n");
    write(sockid, ack_buf, strlen(ack_buf));
    return 0;
}

// GET处理函数
int get_handle(int sockid, http_head *hhead, req_head *rhead, char *data_buf) {
    char *ack_buff = malloc(20*1024*1024);
    char *file_buff = malloc(20*1024*1024);
    memset(ack_buff, 0, 20*1024*1024);
    memset(file_buff, 0, 20*1024*1024);

    int fsize = copy_file(hhead, file_buff);
    if( fsize == -1) {
        // 404-响应头部
        strcpy(ack_buff, hhead->version);
        strcpy(ack_buff + strlen(ack_buff), " 404 Not Found\r\n");
        strcpy(ack_buff + strlen(ack_buff), "Connection: Keep-Alive\r\n");
        printf("Ack-head:\n%s\n", ack_buff);
        strcpy(ack_buff + strlen(ack_buff), "\r\n");
        write(sockid, ack_buff, strlen(ack_buff));
        return 0;
    } else {
        // 有相应的文件存在-此时需要判断是否有Range字段-区分 206 200
        for(int i=0; i < rhead->num; i++) {
            printf("REQ Line INFO:\n");
            printf("%s: %s\n", rhead->line[i].name, rhead->line[i].value);
            if(!strcmp(rhead->line[i].name, "Range")){
                char *str1 = NULL;
                char *str2 = NULL;
                int cnt1 = 0;
                int cnt2 = 0;
                //206-响应报文
                strcpy(ack_buff, hhead->version);
                strcpy(ack_buff + strlen(ack_buff), " 206 Partial Content\r\n");
                strcpy(ack_buff + strlen(ack_buff), "Connection: Keep-Alive\r\n");
                strcat(rhead->line[i].name, ": ");
                strcat(rhead->line[i].name, rhead->line[i].value);
                strcat(rhead->line[i].name, "\r\n");
                strcpy(ack_buff + strlen(ack_buff), rhead->line[i].name);
                
                printf("Ack-head:\n%s\n", ack_buff);
                strcpy(ack_buff + strlen(ack_buff), "\r\n");
                // 分离出需要传回的部分数据 bytes=str1-str2
                char *tmp = strstr(rhead->line[i].value, "=");
                //printf("[Partial]value:%s\n", tmp);
                tmp += 1;
                //printf("[Partial]tmp:%s\n", tmp);
                str1 = strtok(tmp, "-");
                //printf("[Partial]str1:%s\n", str1);
                str2 = strtok(NULL, "-");
                //printf("[Partial]str2:%s\n", str2);
                
                cnt1 = atoi(str1);
                if(str2 != NULL) {
                    cnt2 = atoi(str2);
                    fsize = cnt2 - cnt1 + 1;
                    printf("cnt1:%d cnt:%d fsize:%d\n", cnt1, cnt2, fsize);
                    printf("Client want data bytes %s-%s.\n", str1, str2);
                } else {
                    fsize = fsize - cnt1;
                    printf("Client want start:%d bytes:%d\n", cnt1, fsize);
                }
                
                ack_get(sockid, ack_buff, file_buff+cnt1, fsize);
                return 0;
            }       
        }
        // 200-响应头部头部
        strcpy(ack_buff, hhead->version);
        strcpy(ack_buff + strlen(ack_buff), " 200 OK\r\n");
        strcpy(ack_buff + strlen(ack_buff), "Connection: Keep-Alive\r\n");
        printf("Ack-head:\n%s\n", ack_buff);
        strcpy(ack_buff + strlen(ack_buff), "\r\n");

        ack_get(sockid, ack_buff, file_buff, fsize);
        return 0;
    }

    // // 首先查找服务器是否有请求的文件
    // int fsize = copy_file(hhead, file_buff);
    // if( fsize == -1) {
    //     // 响应头部
    //     strcpy(ack_buff, hhead->version);
    //     strcpy(ack_buff + strlen(ack_buff), " 404 Not Found\r\n");
    //     strcpy(ack_buff + strlen(ack_buff), "Connection: Keep-Alive\r\n");
    //     printf("Ack-head:\n%s\n", ack_buff);
    //     strcpy(ack_buff + strlen(ack_buff), "\r\n");
    // } else {
    //     // 应答报文头部
    //     strcpy(ack_buff, hhead->version);
    //     strcpy(ack_buff + strlen(ack_buff), " 200 OK\r\n");
    //     strcpy(ack_buff + strlen(ack_buff), "Connection: Keep-Alive\r\n");
    //     printf("Ack-head:\n%s\n", ack_buff);
    //     strcpy(ack_buff + strlen(ack_buff), "\r\n");
    // }

    // ack_get(sockid, ack_buff, file_buff, fsize);
    // return 0;
}

int req_handle(int sockid) {
    http_head hhead;
    req_head rhead;
    memset(&hhead, 0, sizeof(http_head));
    memset(&rhead, 0, sizeof(req_head));

    char *data_buffer = malloc(1024);
    memset(data_buffer, 0, 1024);

    // 将socket内容读入缓冲区
    char *buffer = malloc(1024);
    memset(buffer, 0, 1024);
    read(sockid, buffer, 1024);
    printf("REQ:\n%s\n\n", buffer);

    // 解析报文，并且打印出来相应头部信息
    parse_buffer(buffer, &hhead, &rhead, data_buffer);
    printf("Current request information:\n");
    printf("Method:%s\n", hhead.method);
    printf("Url:%s\n", hhead.url);
    printf("Version:%s\n", hhead.version);
    printf("Data:%s\n\n", data_buffer);

    // 处理函数 只需实现GET
    int methid = get_method(hhead.method); // 返回1则是GET
    if(methid == 1)
        get_handle(sockid, &hhead, &rhead, data_buffer);
    else
        printf("Method Error!\n\n");//method不是get，或者方法其他错误

    if(!is_alive(&rhead)) //判断是否需要持久连接
        close(sockid);
    else
        printf("keep-alive.\n");
    
    return 0;
}